#ifndef NIGHT_LAMBDA_DETAIL
#define NIGHT_LAMBDA_DETAIL

namespace night { namespace lambda { namespace detail { 

struct true_type {};
struct false_type {};

template< typename T1, typename T2=false_type, typename T3=false_type, typename T4=false_type, typename T5=false_type >
struct cond { typedef T1 type; };

template< typename T2, typename T3, typename T4, typename T5 >
struct cond<false_type,T2,T3,T4,T5> { typedef T2 type; };

template< typename T3, typename T4, typename T5 >
struct cond<false_type,false_type,T3,T4,T5> { typedef T3 type; };

template< typename T4, typename T5 >
struct cond<false_type,false_type,false_type,T4,T5> { typedef T4 type; };

template< typename T5 >
struct cond<false_type,false_type,false_type,false_type,T5> { typedef T5 type; };

template<>
struct cond<false_type,false_type,false_type,false_type,false_type> { typedef false_type type; };

template< typename T >
struct is_exist 
{ 
	typedef true_type value; 
	typedef T type; 
};

template< typename T, typename where = true_type >
struct is_iterator 
{
	typedef is_iterator type;
	typedef false_type value;

	typedef false_type value_type;	
	typedef false_type pointer;	
	typedef false_type reference;	
};

template< typename Iterator >
struct is_iterator<Iterator,typename detail::is_exist<typename Iterator::reference>::value> 
{
	typedef is_iterator type;
	typedef true_type value;

	typedef typename Iterator::value_type value_type;	
	typedef typename Iterator::pointer pointer;	
	typedef typename Iterator::reference reference;
};

template< typename T >
struct is_iterator<T*> 
{
	typedef is_iterator type;
	typedef true_type value;

	typedef T value_type;
	typedef T * pointer;
	typedef T & reference;
};

template< typename T, typename where = true_type >
struct is_pointer 
{
	typedef is_pointer type;
	typedef false_type value;

	typedef false_type value_type;
	typedef false_type pointer;
	typedef false_type reference;
};

template< typename Pointer >
struct is_pointer<Pointer,typename detail::is_exist<typename Pointer::element_type>::value> 
{
	typedef is_pointer type;
	typedef true_type value;

	typedef typename Pointer::element_type value_type;
	typedef typename Pointer::element_type * pointer;
	typedef typename Pointer::element_type & reference;
};

template< typename T >
struct is_pointer<T*> 
{
	typedef is_pointer type;
	typedef true_type value;

	typedef T value_type;
	typedef T * pointer;
	typedef T & reference;
};

template< typename T, typename where = true_type >
struct is_reference 
{
	typedef is_reference type;
	typedef false_type value;

	typedef false_type value_type;
	typedef false_type pointer;
	typedef false_type reference;
};

template< typename T >
struct is_reference<T&> 
{
	typedef is_reference type;
	typedef true_type value;

	typedef T value_type;
	typedef T * pointer;
	typedef T & reference;
};

template< typename T >
inline T & ref ( T & src ) { return src; }

template< typename T >
inline T const & ref ( T const & src ) { return src; }

template< typename T >
struct remove_ref { typedef T type; };

template< typename T >
struct remove_ref<T&> { typedef T type; };

template< typename T >
struct remove_const { typedef T type; };

template< typename T >
struct remove_const<T const> { typedef T type; };

template< typename T >
struct param {
	typedef typename detail::cond<
		typename detail::is_reference<T>::value_type,
		typename detail::remove_ref<T>::type const
	>::type	type;
};

template< typename L, typename where = true_type >
struct call
{
	template< typename T1=void, typename T2=void, typename T3=void, typename Dummy=void > struct sig {};

	template< typename T1 >
	struct sig<T1> {
		typedef typename L::template sig<T1>::result_type result_type;
	};
	
	template< typename T1, typename T2 >
	struct sig<T1,T2> {
		typedef typename L::template sig<T1,T2>::result_type result_type;
	};

	template< typename T1, typename T2, typename T3 >
	struct sig<T1,T2,T3> {
		typedef typename L::template sig<T1,T2,T3>::result_type result_type;
	};

	typedef typename detail::remove_const<L>::type Expr;

	template< typename T1 >
	static typename sig<T1>::result_type apply ( Expr & fn, T1 & x ) { return fn (x); }

	template< typename T1 >
	static typename sig<T1>::result_type apply ( Expr const & fn, T1 & x ) { return fn (x); }

	template< typename T1, typename T2 >
	static typename sig<T1,T2>::result_type apply ( Expr & fn, T1 & x, T2 & y ) { return fn (x,y); }

	template< typename T1, typename T2 >
	static typename sig<T1,T2>::result_type apply ( Expr const & fn, T1 & x, T2 & y ) { return fn (x,y); }

	template< typename T1, typename T2, typename T3 >
	static typename sig<T1,T2,T3>::result_type apply ( Expr & fn, T1 & x, T2 & y, T3 & z ) { return fn (x,y,z); }

	template< typename T1, typename T2, typename T3 >
	static typename sig<T1,T2,T3>::result_type apply ( Expr const & fn, T1 & x, T2 & y, T3 & z ) { return fn (x,y,z); }

};

template< typename F >
struct call<F&> : call<F> {};

template< typename F >
struct call<F*> : call<F> {};

template< typename F >
struct call<F, typename detail::is_exist<typename F::result_type>::value>
{
	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig {
		typedef typename F::result_type result_type;
	};

	typedef typename detail::remove_const<F>::type Expr;

	template< typename T1 >
	static typename sig<T1>::result_type apply ( Expr & fn, T1 & x ) { return fn (x); }

	template< typename T1 >
	static typename sig<T1>::result_type apply ( Expr const & fn, T1 & x ) { return fn (x); }

	template< typename T1, typename T2 >
	static typename sig<T1,T2>::result_type apply ( Expr & fn, T1 & x, T2 & y ) { return fn (x,y); }

	template< typename T1, typename T2 >
	static typename sig<T1,T2>::result_type apply ( Expr const & fn, T1 & x, T2 & y ) { return fn (x,y); }

	template< typename T1, typename T2, typename T3 >
	static typename sig<T1,T2,T3>::result_type apply ( Expr & fn, T1 & x, T2 & y, T3 & z ) { return fn (x,y,z); }

	template< typename T1, typename T2, typename T3 >
	static typename sig<T1,T2,T3>::result_type apply ( Expr const & fn, T1 & x, T2 & y, T3 & z ) { return fn (x,y,z); }

};

template< typename R, typename S1 >
struct call< R (S1) > 
{
	typedef R (function) (S1);

	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig { typedef R result_type; };

	template< typename T1 >
	static R apply ( function * fn, T1 & x ) { return fn (x); }
};

template< typename R, typename S1, typename S2 >
struct call< R (S1,S2) > 
{
	typedef R (function) (S1,S2);

	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig { typedef R result_type; };

	template< typename T1, typename T2 >
	static R apply ( function * fn, T1 & x, T2 & y ) { return fn (x,y); }
};

template< typename R, typename S1, typename S2, typename S3 >
struct call< R (S1,S2,S3) > 
{
	typedef R (function) (S1,S2,S3);

	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig { typedef R result_type; };

	template< typename T1, typename T2, typename T3 >
	static R apply ( function * fn, T1 & x, T2 & y, T3 & z ) { return fn (x,y,z); }
};

template< typename S, typename R >
struct call< R (S::*) > 
{
	typedef R (S::*field);

	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig { typedef R & result_type; };

	template< typename T1 >
	struct sig < T1 const > { typedef R const & result_type; };
	template< typename T1 >
	struct sig < T1 const * > { typedef R const & result_type; };

	static R & apply ( field f, S & x ) { return x.*f; }
	static R & apply ( field f, S * x ) { return x->*f; }
	static R const & apply ( field f, S const & x ) { return x.*f; }
	static R const & apply ( field f, S const * x ) { return x->*f; }
};

template< typename S, typename R >
struct call< R (S::*) () > 
{
	typedef R (S::*function) ();

	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig { typedef R result_type; };

	static R apply ( function fn, S & x ) { return (x.*fn) (); }
	static R apply ( function fn, S * x ) { return (x->*fn) (); }
};

template< typename S, typename R >
struct call< R (S::*) () const > : call< R (S::*) () >
{
	typedef R (S::*function) () const;

	static R apply ( function fn, S const & x ) { return (x.*fn) (); }
	static R apply ( function fn, S const * x ) { return (x->*fn) (); }
};

template< typename S, typename R, typename S1 >
struct call< R (S::*) (S1) > 
{
	typedef R (S::*function) (S1);

	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig { typedef R result_type; };

	template< typename T1 >
	static R apply ( function fn, S & x, T1 & y ) { return (x.*fn) (y); }
	template< typename T1 >
	static R apply ( function fn, S * x, T1 & y ) { return (x->*fn) (y); }
};

template< typename S, typename R, typename S1 >
struct call< R (S::*) (S1) const > : call< R (S::*) (S1) >
{
	typedef R (S::*function) (S1) const;

	template< typename T1 >
	static R apply ( function fn, S const & x, T1 & y ) { return (x.*fn) (y); }
	template< typename T1 >
	static R apply ( function fn, S const * x, T1 & y ) { return (x->*fn) (y); }
};

template< typename S, typename R, typename S1, typename S2 >
struct call< R (S::*) (S1,S2) > 
{
	typedef R (S::*function) (S1,S2);

	template< typename T1=void, typename T2=void, typename T3=void >
	struct sig { typedef R result_type; };

	template< typename T1, typename T2 >
	static R apply ( function fn, S & x, T1 & y, T2 & z ) { return (x.*fn) (y,z); }
	template< typename T1, typename T2 >
	static R apply ( function fn, S * x, T1 & y, T2 & z ) { return (x->*fn) (y,z); }
};

template< typename S, typename R, typename S1, typename S2 >
struct call< R (S::*) (S1,S2) const > : call< R (S::*) (S1,S2) >
{
	typedef R (S::*function) (S1,S2) const;

	template< typename T1, typename T2 >
	static R apply ( function fn, S const & x, T1 & y, T2 & z ) { return (x.*fn) (y,z); }
	template< typename T1, typename T2 >
	static R apply ( function fn, S const * x, T1 & y, T2 & z ) { return (x->*fn) (y,z); }
};

} } } // end namespace night

namespace night { namespace lambda { namespace detail { 

struct empty {};

template< typename Head = empty, typename Tail = empty >
struct list : Tail
{
	typedef list type;
	typedef Head head_type;
	typedef Tail tail_type;

	head_type & value;

	explicit list ( head_type & src ) : value (src) {}

	template < class T1 >
	explicit list ( head_type & x, T1 & y ) : tail_type (y), value (x) {}

	template < class T1, class T2 >
	explicit list ( head_type & x, T1 & y, T2 & z ) : tail_type (y,z), value (x) {}
	
};

template<> struct list<> 
{
	typedef detail::empty type;
	typedef detail::empty head_type;
	typedef list tail_type;
};

template< typename T1=void, typename T2=void, typename T3=void, typename Dummy=void > struct tuple;

template<> struct tuple<> : detail::list<> {};

template< typename T1 >
struct tuple<T1> : detail::list<T1>
{
	typedef detail::list<T1> list_type;
	explicit tuple ( T1 & x ) : list_type (x) {}
};

template< typename T1, typename T2 >
struct tuple<T1,T2> : detail::list< T1, detail::list<T2> >
{
	typedef detail::list< T1, detail::list<T2> > list_type;
	explicit tuple ( T1 & x, T2 & y ) : list_type (x,y) {}

};

template< typename T1, typename T2, typename T3 >
struct tuple<T1,T2,T3> : detail::list< T1, detail::list< T2, detail::list<T3> > >
{
	typedef detail::list< T1, detail::list< T2, detail::list<T3> > > list_type;
	explicit tuple ( T1 & x, T2 & y, T3 & z ) : list_type (x,y,z) {}

};

template< int i, class Tuple >
struct element 
{
	typedef typename element <i-1,typename Tuple::tail_type>::type type;
	typedef typename element <i-1,typename Tuple::tail_type>::tuple_type tuple_type;
};

template< class Tuple >
struct element<0,Tuple>
{
	typedef typename Tuple::head_type type;
	typedef Tuple tuple_type;
};

template<typename Type> struct disable_const { typedef Type type; };
template<typename Type> struct disable_const<Type const> {};

template< int i, class Tuple >
inline typename detail::element<i,typename detail::disable_const<Tuple>::type>::type & get ( Tuple & t ) { 
	return static_cast< typename detail::element<i,Tuple>::tuple_type & > (t).value; 
}

template< int i, class Tuple >
inline typename detail::element<i,Tuple>::type & get ( Tuple const & t ) { 
	return static_cast< typename detail::element<i,Tuple>::tuple_type const & > (t).value; 
}

inline detail::tuple<> make_tuple () { return detail::tuple<> (); }

template< typename T1 >
inline detail::tuple<T1> make_tuple ( T1 & x ) { return detail::tuple<T1> (x); }

template< typename T1, typename T2 >
inline detail::tuple<T1,T2> make_tuple ( T1 & x, T2 & y ) { return detail::tuple<T1,T2> (x,y); }

template< typename T1, typename T2, typename T3 >
inline detail::tuple<T1,T2,T3> make_tuple ( T1 & x, T2 & y, T3 & z ) { return detail::tuple<T1,T2,T3> (x,y,z); }

template< typename T1, typename T2 >
struct join 
{
	typedef detail::tuple<T1,T2> result_type;
	static result_type apply ( T1 & x, T2 & y ) { return result_type (x,y); }
};

template< typename T1, typename T2, typename T3 >
struct join< detail::tuple<T1,T2> const, T3 >
{
	typedef detail::tuple<T1,T2,T3> result_type;
	static result_type apply ( detail::tuple<T1,T2> const & vars, T3 & x ) { 
		return result_type ( detail::get<0> (vars), detail::get<1> (vars), x );
	}
};

} } } // end namespace night

#endif // NIGHT_LAMBDA_DETAIL
